package com.example.xmhds_1.ws;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.xmhds_1.common.cache.UserWebCacheManager;
import com.example.xmhds_1.service.ChatService;
import com.example.xmhds_1.service.UserAccountService;
import com.example.xmhds_1.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.alibaba.fastjson.JSONObject;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.*;
/**
 * WebSocket服务
 * 处理所有WebSocket连接和消息
 */
@Slf4j
@Component
@ServerEndpoint("/ws/login")  // 修改这里，添加路径参数
public class WebSocket {
    public String name;
    public String sessionId;  // 添加sessionId字段
    //注入线程池
    private static ScheduledExecutorService heartbeatScheduler;
    @Autowired
    public void setHeartbeatScheduler(ScheduledExecutorService heartbeatScheduler) {
        WebSocket.heartbeatScheduler = heartbeatScheduler;
    }
    //心跳超时时间，单位为毫秒
    private static final long HEARTBEAT_TIMEOUT = 40 * 1000; // 60秒
    //最后一次收到心跳的时间
    private long lastHeartbeat;
    //心跳定时器
    private ScheduledFuture<?> heartbeatFuture;
    private Session session;
    private static final ConcurrentHashMap<String, Session> ONLINE_SESSIONS = new ConcurrentHashMap<>();
    private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
    private static UserAccountService userAccountService;

    /**
     * 用户相关的WebSocket处理类
     */
    private static final UserWebSocket userWebSocket = new UserWebSocket();

    /**
     * 聊天相关的WebSocket处理类
     */
    private static final ChatWebSocket chatWebSocket = new ChatWebSocket();

    /**
     * 注入UserAccountService
     * 使用static方法以便在WebSocket实例创建时注入
     */
    @Autowired
    public static void setUserAccountService(UserAccountService service) {
        WebSocket.userAccountService = service;
        UserWebSocket.userAccountService = service;
    }

    @Autowired
    public void setChatService(ChatService chatService) {
        ChatWebSocket.chatService = chatService;
    }

    @Autowired
    public void setUserInfoService(UserInfoService userInfoService) {
        ChatWebSocket.userInfoService = userInfoService;
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("sessionId") String sessionIds) {
        try{
            if(UserWebCacheManager.getWeb(sessionIds)!=null){
                    this.name=UserWebCacheManager.getWeb(sessionIds);
                    this.sessionId=sessionIds;
                }else {
                    this.name = webSockets.size() + "";
                    }
            this.session=session;
            webSockets.add(this);
            log.info("【WebSocket】有新的连接,总数为:{}", webSockets.size());

            updateHeartbeat();//更新心跳时间
            startHeartbeatCheck(); //启动心跳检测
        }catch (Exception e){
            log.error("WebSocket连接失败: {}", e);
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {

        log.info(this.name+"收到消息: {}", message);
        JSONObject request = JSON.parseObject(message);
        JSONObject response = new JSONObject();
        response.put("type", request.getString("type"));

        try {
            switch (request.getString("type")) {
                //心跳
                case "ping":
                    handlePing();
                    break;
                //登入
                case "login":
                    response = userWebSocket.handleLogin(request, response);
                    //登入成功后,当前名称就是用户账号
                    if (response.getBooleanValue("success")) {
                        //this.name=request.getString("username");
                        for(WebSocket webSocket:webSockets){
                            if(webSocket.name.equals(this.name)){
                                webSockets.remove(webSocket);
                                this.name=request.getString("username");
                                if(UserWebCacheManager.getWeb(sessionId)!=null) {
                                    UserWebCacheManager.removeWeb(sessionId);
                                }
                                UserWebCacheManager.saveWeb(sessionId, this.name);
                                webSockets.add(this);
                                break;
                            }
                        }
                    }

                    break;
                //注册
                case "register":
                    response = userWebSocket.handleRegister(request, response);
                    break;
                //获取聊天记录
                case "getChatList":
                    response = chatWebSocket.handleGetChatList(request, response);
                    break;
                //获取对应好友的聊天记录
                case "getChatHistory":
                    response = chatWebSocket.handleGetGroupChatHistory(request, response);
                    break;
                //已读私发的聊天记录
                case "updateGroupChatRead":
                    response = chatWebSocket.handleUpdateGroupChatRead(request, response);
                    break;
                //发送消息
                case "sendGroupChat":
                    response = chatWebSocket.handleSendGroupChat(request, response);
                    // 如果发送成功，还需要通知接收者
                   /* if (response.getBooleanValue("success")) {
                        notifyReceiver(request.getString("to"), request);
                    }*/
                    break;
                default:
                    response.put("success", false);
                    response.put("message", "未知的消息类型");
                    break;
            }
        } catch (Exception e) {
            log.error("处理消息出错", e);
            response.put("success", false);
            response.put("message", "服务器内部错误");
        }

        sendMessage(session, response.toJSONString());
    }

    @OnClose
    public void onClose(Session session) {
        try{
            webSockets.remove(this);
            stopHeartbeatCheck();
            log.info("【WebSocket】连接断开,总数为:{}", webSockets.size());
        }catch (Exception e){
            log.error("WebSocket关闭异常: {}", e);
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket错误", error);
        sendError(session, "发生错误");
    }

    private void sendMessage(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("发送消息失败", e);
        }
    }

    private void sendError(Session session, String errorMessage) {
        JSONObject response = new JSONObject();
        response.put("type", "error");
        response.put("message", errorMessage);
        sendMessage(session, response.toJSONString());
    }
    /**
     * 断开连接后,又有新的连接,发现是断开连接的那个
     */

    /**
     * 通知消息接收者
     */
    private void notifyReceiver(String receiverId, JSONObject message) {
        Session receiverSession = ONLINE_SESSIONS.get(receiverId);
        if (receiverSession != null && receiverSession.isOpen()) {
            JSONObject notification = new JSONObject();
            notification.put("type", "newMessage");
            notification.put("data", message);
            sendMessage(receiverSession, notification.toJSONString());
        }
    }
    /**
     * 处理客户端发来的心跳ping消息
     * 发送pong响应并更新最后心跳时间
     */
    private void handlePing() {
        try {
            // 创建心跳响应JSON对象
            JSONObject pong = new JSONObject();
            // 设置消息类型为"pong"
            pong.put("type", "pong");
            pong.put("success", true);  // 添加成功标志
            // 添加服务器当前时间戳
            // 客户端可用于计算往返延迟
            pong.put("time", System.currentTimeMillis());

            // 使用当前session发送pong响应给客户端
            sendMessage(this.session, pong.toJSONString());
            // 更新服务端的最后心跳时间
            updateHeartbeat();

        } catch (Exception e) {
            // 记录错误日志
            log.error("发送心跳响应失败", e);
        }
    }
    /**
     * 更新最后一次心跳时间
     * 每次收到客户端消息时都会调用此方法更新时间戳
     */
    private void updateHeartbeat() {
        // 将最后心跳时间更新为当前时间戳
        this.lastHeartbeat = System.currentTimeMillis();
    }

    /**
     * 启动心跳检测定时任务
     * 定期检查客户端连接是否存活
     */
    private void startHeartbeatCheck() {
        // scheduleAtFixedRate: 以固定频率执行任务
        // 参数说明:
        // 1. 要执行的任务(lambda表达式)
        // 2. 首次执行延迟时间(10秒)
        // 3. 任务执行间隔时间(10秒)
        // 4. 时间单位(秒)
        this.heartbeatFuture = heartbeatScheduler.scheduleAtFixedRate(() -> {
            try {
                // 获取当前时间戳
                long now = System.currentTimeMillis();
                // 如果当前时间与最后心跳时间的差值超过了超时时间
                if (now - lastHeartbeat > HEARTBEAT_TIMEOUT) {
                    // 记录警告日志
                    log.warn("心跳超时，关闭连接");
                    if(UserWebCacheManager.getWeb(sessionId) != null) {
                        UserWebCacheManager.removeWeb(sessionId);
                    }
                    // 关闭WebSocket会话
                    session.close();
                }
            } catch (Exception e) {
                // 记录错误日志
                log.error("心跳检查失败", e);
            }
        }, 10, 10, TimeUnit.SECONDS);
    }

    /**
     * 停止心跳检测
     * 在连接关闭时调用，清理定时任务
     */
    private void stopHeartbeatCheck() {
        // 如果定时任务存在且未被取消
        if (heartbeatFuture != null && !heartbeatFuture.isCancelled()) {
            // 取消定时任务
            // 参数true表示即使任务正在执行也立即中断
            heartbeatFuture.cancel(true);
        }
    }
    /**
     * 通过账号查询对应的WebSocket对象
     */
    public static WebSocket getWebSocketByAccount(String account) {
        for(WebSocket webSocket : webSockets) {
            if(webSocket.name.equals(account)) {
                return webSocket;
            }
        }
        return null;
    }
    /**
     * 向指定用户发送通知
     * 如果用户在线，则发送通知消息
     * 如果用户不在线，则不发送
     * 响应格式为{"type":"newMessage","from":"发送者账号","message":"通知内容","time":"发送时间"}
     */
    public static void notifyUser(String userId, String message) {

        WebSocket client=getWebSocketByAccount(userId);
        if (client != null&& client.session.isOpen())  {
            try{
                client.session.getBasicRemote().sendText(message);
                log.info("向用户{}发送通知：{}", userId, message);
            }catch (Exception e){
                log.error("向用户{}发送通知失败：{}", userId, e.getMessage());
            }
        }else {
            log.info("用户{}不在线", userId);
        }
    }
}
